home *** CD-ROM | disk | FTP | other *** search
- /* from the TOS GCC library */
- /* malloc, free, realloc: dynamic memory allocation */
- /* ERS: added mlalloc, relalloc, etc. for 16 bit compilers. Changed
- argument of malloc, etc., to size_t (per ANSI draft). */
- /* jrb: added support for allocation from _heapbase when _stksize == -1
- thanks to Piet van Oostrum & Atze Dijkstra for this idea and
- their diffs. */
- /* Added malloc_init and warnfunction for emacs support
- * This will only work if you set _stksize = -1. Otherwise we have no
- * correct lim_data and data_bytes_unused
- * [[ i have a solution for this, but i included this stuff for emacs only ]]
- * shrink heap if free'd block is at the top of the heap
- * added sbrk (works either with heap or Malloc) (er)
- */
-
- #include <stddef.h> /* for size_t */
- #include <memory.h>
- #ifndef __MINT__
- #include <unixlib.h>
- #else
- #include <unistd.h>
- #endif
- #include <string.h>
- #include <osbind.h>
- #include <assert.h>
-
- extern void *_heapbase;
- extern long _stksize;
-
- /* minimum chunk to ask OS for */
- static size_t MINHUNK = 4096L; /* default */
- static size_t MAXHUNK = 16*1024L; /* max. default */
-
- /* CAUTION: use _mallocChunkSize() to tailor to your environment,
- do not make the default too large, as the compiler
- gets screwed on a 1M machine otherwise (stack/heap clash)
- */
-
- struct mem_chunk
- {
- struct mem_chunk *next;
- long size;
- };
-
- /* linked list of free blocks */
-
- static struct mem_chunk _mchunk_free_list = { NULL, 0L };
-
- /* flag to control zero'ing of malloc'ed chunks */
- static int _ZeroMallocs = 0;
-
- /* Number of bytes of writable memory we can expect to be able to get */
- extern long __lim_data; /* in sbrk.c */
- extern long __malloc_bytes_in_free_list; /* in sbrk.c */
- extern long malloc_sbrk_used; /* in sbrk.c */
- extern long malloc_sbrk_unused; /* in sbrk.c */
-
- /* Level number of warnings already issued.
- 0 -- no warnings issued.
- 1 -- 75% warning already issued.
- 2 -- 85% warning already issued.
- */
- static int warnlevel;
-
- /* Function to call to issue a warning;
- 0 means don't issue them. */
- static void (*warnfunction) ();
-
- /* Cause reinitialization based on job parameters;
- also declare where the end of pure storage is. */
- void
- malloc_init (start, warnfun)
- void *start;
- void (*warnfun) ();
- {
- __lim_data = 0;
- __lim_data += (long) sbrk(0) - (long) start;
- warnlevel = 0;
- if (warnfun != (void (*)) -1)
- warnfunction = warnfun;
- }
-
- static void
- do_warning(void)
- {
- if(warnfunction)
- switch (warnlevel) {
- case 0:
- if (malloc_sbrk_used > (__lim_data / 4) * 3) {
- warnlevel++;
- (*warnfunction) ("Warning: past 75% of memory limit\n");
- }
- break;
- case 1:
- if (malloc_sbrk_used > (__lim_data / 20) * 17) {
- warnlevel++;
- (*warnfunction) ("Warning: past 85% of memory limit\n");
- }
- break;
- case 2:
- if (malloc_sbrk_used > (__lim_data / 20) * 19) {
- warnlevel++;
- (*warnfunction) ("Warning: past 95% of memory limit\n");
- }
- break;
- }
- }
-
- asm(".text; .even; .globl _mlalloc; _mlalloc:");
-
- void * malloc(n)
- size_t n;
- {
- struct mem_chunk *p, *q;
- long sz;
-
- /* add a mem_chunk to required size and round up */
- n = n + sizeof(struct mem_chunk);
- n = (7 + n) & ~7;
- /* look for first block big enough in free list */
- p = &_mchunk_free_list;
- q = _mchunk_free_list.next;
-
- while ((q != NULL) && (q->size < n))
- {
- p = q;
- q = q->next;
- }
-
- /* if not enough memory, get more from the system */
- if (q == NULL)
- {
- if ((_heapbase != NULL) || (n > MINHUNK))
- sz = n;
- else {
- sz = MINHUNK;
- if (MINHUNK < MAXHUNK)
- MINHUNK *= 2;
- }
- q = (struct mem_chunk * )lsbrk(sz);
-
- if (((long)q) <= 0) /* can't alloc any more? */
- return(NULL);
-
- p->next = q;
- q->size = sz;
- q->next = NULL;
- }
-
- if (q->size > n + sizeof(struct mem_chunk))
- { /* split, leave part of free list */
- q->size -= n;
- q = (struct mem_chunk * )(((long) q) + q->size);
- q->size = n;
- }
- else
- { /* just unlink it */
- p->next = q->next;
- }
-
- malloc_sbrk_used += q->size;
- malloc_sbrk_unused -= q->size;
- __malloc_bytes_in_free_list -= q->size;
- do_warning();
- q->next = NULL;
- q++; /* hand back ptr to after chunk desc */
- if(_ZeroMallocs != 0)
- #ifndef __MINT__
- lbzero((void *)q, (long)(n - sizeof(struct mem_chunk)));
- #else
- bzero((void *)q, (long)(n - sizeof(struct mem_chunk)));
- #endif
- return((void * )q);
- }
-
- void free(param)
- void *param;
- {
- struct mem_chunk *o, *p, *q, *s;
- struct mem_chunk *r = (struct mem_chunk *) param;
-
- /* free(NULL) should do nothing */
- if (r == 0)
- return;
-
- /* move back to uncover the mem_chunk */
- r--; /* there it is! */
-
- assert(!(r->size & 7));
- malloc_sbrk_used -= r->size;
- malloc_sbrk_unused += r->size;
- __malloc_bytes_in_free_list += r->size;
- /* lbzero(r + 1,r->size - sizeof(struct mem_chunk)); */
-
- /* stick it into free list, preserving ascending address order */
- o = NULL;
- p = &_mchunk_free_list;
- q = _mchunk_free_list.next;
- while (q != NULL && q < r)
- {
- o = p;
- p = q;
- q = q->next;
- }
-
- /* merge after if possible */
- s = (struct mem_chunk * )(((long) r) + r->size);
- if (q != NULL && s >= q)
- {
- assert(s == q);
- r->size += q->size;
- q = q->next;
- s->size = 0;
- s->next = NULL;
- }
- r->next = q;
-
- /* merge before if possible, otherwise link it in */
- s = (struct mem_chunk * )(((long) p) + p->size);
- if (s >= r && p != &_mchunk_free_list)
- /* remember: r may be below &_mchunk_free_list in memory */
- {
- assert(s == r);
- p->size += r->size;
- p->next = r->next;
- r->size = 0;
- r->next = NULL;
- s = (struct mem_chunk * )(((long) p) + p->size);
- if (_heapbase != NULL && s >= (struct mem_chunk *) _heapbase) {
- assert(s == _heapbase);
- lsbrk(-p->size);
- o->next = NULL; /* o is always != NULL here */
- }
- }
- else
- {
- s = (struct mem_chunk * )(((long) r) + r->size);
- if (_heapbase != NULL && s >= (struct mem_chunk *) _heapbase) {
- assert(s == _heapbase);
- lsbrk(-r->size);
- p->next = NULL;
- } else p->next = r;
- }
- }
-
- asm(".text; .even; .globl _relalloc,_realloc; _relalloc: jra _realloc");
- #ifdef NDEBUG
- static char *__dummy = ""; /* otherwise we get a bra with 0 offset above */
- #endif
-
- void * realloc(_r, n)
- void *_r;
- size_t n;
- {
- struct mem_chunk *p, *q, *r = _r;
- long *src, *dst;
- long sz;
-
- /* obscure features: realloc(NULL,n) is the same as malloc(n)
- * realloc(p, 0) is the same as free(p)
- */
- if (!r)
- return malloc(n);
- if (n == 0) {
- free(_r);
- return NULL;
- }
- p = r - 1;
- sz = (n + sizeof(struct mem_chunk) + 7) & ~7;
-
- if (p->size > sz)
- { /* block too big, split in two */
- q = (struct mem_chunk * )(((long) p) + sz);
- q->size = p->size - sz;
- free(q + 1);
- p->size = sz;
- }
- else
- if (p->size < sz)
- { /* block too small, get new one */
- struct mem_chunk *s, *t;
- q = &_mchunk_free_list;
- t = _mchunk_free_list.next;
- while (t != NULL && t < p)
- {
- q = t;
- t = t->next;
- }
-
- /* merge after if possible */
- s = (struct mem_chunk * )(((long) p) + p->size);
- if (t != NULL && s >= t && p->size + t->size >= sz)
- {
- assert(s == t);
- p->size += t->size;
- q->next = t->next;
- malloc_sbrk_used += t->size;
- malloc_sbrk_unused -= t->size;
- __malloc_bytes_in_free_list -= t->size;
- do_warning();
- t->size = 0;
- t->next = NULL;
- }
- else
- {
- dst = (long *) q = (struct mem_chunk * )malloc(n);
- if (q != NULL)
- {
- src = (long * )r;
- n = (p->size - sizeof(struct mem_chunk)) >> 2;
- while (n--)
- {
- *dst++ = *src++;
- }
- free(r); /* free r only if we got a new block */
- }
- /* else we could try to mlalloc the rest and hope that we can merge */
- r = q;
- }
- }
- /* else current block will do just fine */
- return((void * )r);
- }
-
- asm(".text; .even; .globl _clalloc; _clalloc:");
- void * calloc(n, sz)
- size_t n, sz;
- {
- char *r;
- size_t total;
-
- total = n * sz;
- if ((r = malloc(total)) != NULL)
- {
- #ifndef __MINT__
- lbzero(r, total);
- #else
- bzero(r, total);
- #endif
- }
- return(r);
- }
-
- /*
- * Set zero block after malloc flag
- */
- void _malloczero(yes)
- int yes;
- {
- _ZeroMallocs = yes;
- }
-
- /*
- * tune chunk size
- */
- void _mallocChunkSize (siz)
- size_t siz;
- {
- MAXHUNK = MINHUNK = siz;
- }
-